﻿define(['services/logger'], function (logger) {

    var calcModel = function (address, netmask, range, useableips, hosts) {
        this.address = address;
        this.netmask = netmask;
        this.range = range;
        this.useableips = useableips;
        this.hosts = hosts;
    };

    var calculator = {
        generateSubnet: generateSubnet,
        updateNetwork: updateNetwork,
        addNetwork: addNetwork
    };
    return calculator;

    function generateSubnet(list) {
        return $.ajax({
            url: '/AWSTool/TestData'
        }).done(function (data) {
            list.push(new calcModel(data, data, data, data, data));
        });
    }

    function addNetwork() {
        //return $.getJSON('/HotTowel/TestData', function (data) {            
        //list.push(new calcModel(data, data, data, data, data));            
        //});
        var dataIn = '{' + '"IdCompeticao":"' + 1 + '"}';
        $.ajax({
            type: 'POST',
            url: '/AWSTool/AddNetwork',
            data: dataIn
        }).done(function myfunction() {
            alert('success');
        });
        return 'Test';
    }

    //#region Calcs

    var curNetwork = 0;
    var curMask = 0;
    var rootSubnet;

    function updateNetwork(network, netbits) {
        var newNetworkStr = network;
        var newMask = netbits;
        var newNetwork = inet_aton(newNetworkStr);
        if (newNetwork === null) {
            logger.logError('Invalid network address entered', null, null, true);
            return null;
        }
        var tmpNetwork = network_address(newNetwork, newMask);
        if (newNetwork != tmpNetwork) {
            logger.logError('The network address entered is not on a network boundary for this mask.\nIt has been changed to ' + inet_ntoa(tmpNetwork) + '.', null, null, true);
            newNetwork = tmpNetwork;
            network = inet_ntoa(tmpNetwork);
        }
        if (newMask < 0 || newMask > 32) {
            logger.logError('The network mask you have entered is invalid', null, null, true);
            return null;
        }
        if (curMask == 0) {
            curMask = newMask;
            curNetwork = newNetwork;
            startOver();
        }
        else if (curMask != newMask && confirm('You are changing the base network from /' + curMask + ' to /' + newMask + '. This will reset any changes you have made. Proceed?')) {
            curMask = newMask;
            curNetwork = newNetwork;
            startOver();
        }
        else {
            netbits = curMask;
            curNetwork = newNetwork;
            //recreateTables();

            var subnet = inet_ntoa(curNetwork) + '/' + curMask;

            var addressFirst = curNetwork;
            var addressLast = subnet_last_address(curNetwork, curMask);
            var useableFirst = curNetwork + 1;
            var useableLast = addressLast - 1;
            var numHosts;
            var addressRange;
            var useableRange;
            if (curMask == 32) {
                addressRange = inet_ntoa(addressFirst);
                useableRange = addressRange;
                numHosts = 1;
            }
            else {
                addressRange = inet_ntoa(addressFirst) + ' - ' + inet_ntoa(addressLast);
                if (curMask == 31) {
                    useableRange = addressRange;
                    numHosts = 2;
                }
                else {
                    useableRange = inet_ntoa(useableFirst) + ' - ' + inet_ntoa(useableLast);
                    numHosts = (1 + useableLast - useableFirst);
                }
            }
            return new calcModel(subnet, inet_ntoa(subnet_netmask(curMask)), addressRange, useableRange, numHosts);
        }
    }

    function startOver() {
        rootSubnet = [0, 0, null];
        //recreateTables();
    }

    function recreateTables() {
        //var calcbody = document.getElementById('calcbody');
        //if (!calcbody) {
        //    alert('Body not found');
        //    return;
        //}

        //while (calcbody.hasChildNodes()) {
        //    calcbody.removeChild(calcbody.firstChild);
        //}

        updateNumChildren(rootSubnet);
        updateDepthChildren(rootSubnet);

        createRow(calcbody, rootSubnet, curNetwork, curMask, [curMask, rootSubnet[1], rootSubnet], rootSubnet[0]);

        document.getElementById('joinHeader').colSpan = (rootSubnet[0] > 0 ? rootSubnet[0] : 1);
        document.getElementById('col_join').span = (rootSubnet[0] > 0 ? rootSubnet[0] : 1);

        /* Create the bookmark hyperlink */
        var link = document.getElementById('saveLink');
        if (link) {
            link.href = 'subnets.html?network=' + inet_ntoa(curNetwork) + '&mask=' + curMask + '&division=' + binToAscii(nodeToString(rootSubnet));
        }
    }

    function createRow(calcbody, node, address, mask, labels, depth) {
        if (node[2]) {
            var newlabels = labels;
            newlabels.push(mask + 1);
            newlabels.push(node[2][0][1]);
            newlabels.push(node[2][0]);
            createRow(calcbody, node[2][0], address, mask + 1, newlabels, depth - 1);

            newlabels = new Array();
            newlabels.push(mask + 1);
            newlabels.push(node[2][1][1]);
            newlabels.push(node[2][1]);
            createRow(calcbody, node[2][1], address + subnet_addresses(mask + 1), mask + 1, newlabels, depth - 1);
        }
        else {
            var newRow = document.createElement('TR');
            calcbody.appendChild(newRow);

            /* subnet address */
            var newCell = document.createElement('TD');
            newCell.appendChild(document.createTextNode(inet_ntoa(address) + '/' + mask));
            newRow.appendChild(newCell);

            var addressFirst = address;
            var addressLast = subnet_last_address(address, mask);
            var useableFirst = address + 1;
            var useableLast = addressLast - 1;
            var numHosts;
            var addressRange;
            var useableRange;

            if (mask == 32) {
                addressRange = inet_ntoa(addressFirst);
                useableRange = addressRange;
                numHosts = 1;
            }
            else {
                addressRange = inet_ntoa(addressFirst) + ' - ' + inet_ntoa(addressLast);
                if (mask == 31) {
                    useableRange = addressRange;
                    numHosts = 2;
                }
                else {
                    useableRange = inet_ntoa(useableFirst) + ' - ' + inet_ntoa(useableLast);
                    numHosts = (1 + useableLast - useableFirst);
                }
            }

            /* netmask */
            var newCell = document.createElement('TD');
            newCell.appendChild(document.createTextNode(inet_ntoa(subnet_netmask(mask))));
            newRow.appendChild(newCell);

            /* range of addresses */
            var newCell = document.createElement('TD');
            newCell.appendChild(document.createTextNode(addressRange));
            newRow.appendChild(newCell);

            /* useable addresses */
            var newCell = document.createElement('TD');
            newCell.appendChild(document.createTextNode(useableRange));
            newRow.appendChild(newCell);

            /* Hosts */
            var newCell = document.createElement('TD');
            newCell.appendChild(document.createTextNode(numHosts));
            newRow.appendChild(newCell);

            /* actions */

            var newCell = document.createElement('TD');
            newRow.appendChild(newCell);

            if (mask == 32) {
                var newLink = document.createElement('SPAN');
                newLink.className = 'disabledAction';
                newLink.appendChild(document.createTextNode('Divide'));
                newCell.appendChild(newLink);
            }
            else {
                var newLink = document.createElement('A');
                newLink.href = '#';
                newLink.onclick = function () { divide(node); return false; }
                newLink.appendChild(document.createTextNode('Divide'));
                newCell.appendChild(newLink);
            }

            var colspan = depth - node[0];

            for (var i = (labels.length / 3) - 1; i >= 0; i--) {
                var mask = labels[i * 3];
                var rowspan = labels[(i * 3) + 1];
                var joinnode = labels[(i * 3) + 2];

                var newCell = document.createElement('TD');
                newCell.rowSpan = (rowspan > 1 ? rowspan : 1);
                newCell.colSpan = (colspan > 1 ? colspan : 1);

                if (i == (labels.length / 3) - 1) {
                    newCell.className = 'maskSpan';
                }
                else {
                    newCell.className = 'maskSpanJoinable';
                    newCell.onclick = newJoin(joinnode);
                    //	newCell.onmouseover = function() { window.status = joinnode[0]+'---'+joinnode[1]+'---'+joinnode[2]+'>>>>>'+node[2];}
                }

                var newImg = document.createElement('IMG');
                newImg.src = 'img/' + mask + '.gif';
                newCell.appendChild(newImg);
                newRow.appendChild(newCell);

                colspan = 1; // reset for subsequent cells
            }
        }

    }

    function updateNumChildren(node) {
        if (node[2] == null) {
            node[1] = 0;
            return 1;
        }
        else {
            node[1] = updateNumChildren(node[2][0]) + updateNumChildren(node[2][1]);
            return node[1];
        }
    }

    function updateDepthChildren(node) {
        if (node[2] == null) {
            node[0] = 0;
            return 1;
        }
        else {
            node[0] = updateDepthChildren(node[2][0]) + updateDepthChildren(node[2][1]);
            return node[1];
        }
    }

    function nodeToString(node) {
        if (node[2]) {
            return '1' + nodeToString(node[2][0]) + nodeToString(node[2][1]);
        }
        else {
            return '0';
        }
    }

    function binToAscii(str) {
        var curOut = '';
        var curBit = 0;
        var curChar = 0;

        for (var i = 0; i < str.length; i++) {
            if (str.charAt(i) == '1') {
                curChar |= 1 << curBit;
            }
            curBit++;
            if (curBit > 3) {
                curOut += curChar.toString(16);
                curChar = 0;
                curBit = 0;
            }
        }
        if (curBit > 0) {
            curOut += curChar.toString(16);
        }
        return str.length + '.' + curOut;
    }

    function asciiToBin(str) {
        var re = /([0-9]+)\.([0-9a-f]+)/;
        var res = re.exec(str);
        var len = res[1];
        var encoded = res[2];
        var out = '';
        for (var i = 0; i < res[1]; i++) {
            var ch = parseInt(res[2].charAt(Math.floor(i / 4)), 16);
            var pos = i % 4;
            out += (ch & (1 << pos) ? '1' : '0');
        }
        return out;
    }

    function inet_aton(addrstr) {
        var re = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$/;
        var res = re.exec(addrstr);
        if (res === null) {
            return null;
        }
        for (var i = 1; i <= 4; i++) {
            if (res[i] < 0 || res[i] > 255) {
                return null;
            }
        }
        return (res[1] << 24) | (res[2] << 16) | (res[3] << 8) | res[4];
    }

    function inet_ntoa(addrint) {
        return ((addrint >> 24) & 0xff) + '.' +
          ((addrint >> 16) & 0xff) + '.' +
          ((addrint >> 8) & 0xff) + '.' +
          (addrint & 0xff);
    }

    function network_address(ip, mask) {
        var maskbits = 0;
        for (var i = 31 - mask; i >= 0; i--) {
            ip &= ~1 << i;
        }
        return ip;
    }

    function subnet_addresses(mask) {
        return 1 << (32 - mask);
    }

    function subnet_last_address(subnet, mask) {
        return subnet + subnet_addresses(mask) - 1;
    }

    function subnet_netmask(mask) {
        return network_address(0xffffffff, mask);
    }
    //#endregion
});